#include "StdAfx.h"
#include "Manager.h"
//@author A0091791X
bool Manager::_existed = false;
Manager* Manager::ONE = NULL;

Manager::Manager(list<Transaction>* recordAddr, list<string>* catAddr, list<string>* accAddr, list<double>* accDepAddr, int* SN)
{
	_recordAddr = recordAddr;
	_accAddr = accAddr;
	_catAddr = catAddr;
	_accDepAddr = accDepAddr;
	_SN = SN;
}
Manager::~Manager()
{
}

//singelton getter
Manager* Manager::getOne(list<Transaction>* recordAddr, list<string>* catAddr, list<string>* accAddr, list<double>* accDepAddr, int* SN)
{
	if(!_existed)
	{
		ONE = new Manager(recordAddr, catAddr, accAddr, accDepAddr, SN);
		_existed = true;
	}
	return ONE;
}
Manager* Manager::get()
{
	return ONE;
}

//getter
list<Transaction>* Manager::getRecordAddr()
{
	return _recordAddr;
}
list<string>* Manager::getAccAddr()
{
	return _accAddr;
}
list<string>* Manager::getCatAddr()
{
	return _catAddr;
}
list<double>* Manager::getAccDep()
{
	return _accDepAddr;
}
int* Manager::getSNAddr()
{
	return _SN;
}
stack<Action>* Manager::getActionStack()
{
	return &_action;
}
int Manager::getActionStackSize()
{
	return _action.size();
}
list<double>::iterator Manager::getAccDepIter(string FIND)
{
	list<string>::iterator iterA;
	list<double>::iterator iterD;
	for(iterA=_accAddr->begin(), iterD=_accDepAddr->begin(); iterA!=_accAddr->end(); iterA++,iterD++)
	{
		if(*iterA == FIND)
			return iterD;
	}
	return iterD;
}

list<Transaction>::iterator Manager::findTransaction(int SN)
{
	list<Transaction>::iterator iterT;
	for(iterT=_recordAddr->begin(); iterT!=_recordAddr->end(); iterT++)
	{
		if(iterT->getSerialNo()==SN)
			return iterT;
	}
	return iterT;
}
list<string>::iterator Manager::findAccount(string acc)
{
	list<string>::iterator iterA;
	for(iterA=_accAddr->begin(); iterA!=_accAddr->end(); iterA++)
	{
		if(*iterA==acc)
			return iterA;
	}
	return iterA;
}//duplicate
list<string>::iterator Manager::findCategory(string cat)
{
	list<string>::iterator iterC;
	for(iterC=_catAddr->begin(); iterC!=_catAddr->end(); iterC++)
	{
		if(*iterC==cat)
			return iterC;
	}
	return iterC;
}

	//@author A0066758M
//add/delete transaction
Transaction* Manager::addTransaction(Transaction* T)
{
	if(T->getType()!=INCOME
		&& T->getType()!=EXPENSE
		&& T->getType()!=TRANSFER)
		return false;

	Transaction *temp;
	_recordAddr->push_back(*T);
	temp = &(_recordAddr->back());
	pushUndo(ADD, *temp);//undo
	incrementSN();
	return temp;
}
bool Manager::editTransaction(int SN, string acct, dateTime DT, double amount)
{
	//for income & expense
	list<Transaction>::iterator iterT;
	iterT=findTransaction(SN);
	if(iterT==_recordAddr->end())
		return false;
	pushUndo(EDIT, *iterT);
	iterT->setAccount(acct);
	iterT->setAmount(amount);
	iterT->setDateTime(DT);
	return true;
}
bool Manager::editTransaction(int SN, string Facct, string Tacct, dateTime DT, double amount)
{
	list<Transaction>::iterator iterT;
	iterT=findTransaction(SN);
	if(iterT==_recordAddr->end())
		return false;
	pushUndo(EDIT, *iterT);//undo
	iterT->setAccountFrom(Facct);
	iterT->setAccountTo(Tacct);
	iterT->setAmount(amount);
	iterT->setDateTime(DT);
	return true;

}
bool Manager::editTransaction(int SN, Transaction T)
{
	if(T.getType()==INCOME || T.getType()==EXPENSE)
		return editTransaction(SN, T.getAccount(), T.getDateTime(), T.getAmount());
	if(T.getType()==TRANSFER)
		return editTransaction(SN, T.getAccountFrom(), T.getAccountTo(), T.getDateTime(), T.getAmount());
}
bool Manager::delTransaction(list<Transaction>::iterator iterT)
{
	if(iterT==_recordAddr->end())
		return false;
	else
	{
		pushUndo(DELETE, *iterT);//undo
		_recordAddr->erase(iterT);
		return true;
	}
}
bool Manager::delTransaction(Transaction* T) 
{
	if(_recordAddr->empty())
		return false;

	list<Transaction>::iterator iterT;
	for(iterT=_recordAddr->begin(); iterT!=_recordAddr->end(); iterT++)
	{
		if(&(*iterT)==T)
		{
			pushUndo(DELETE, *iterT);//undo
			_recordAddr->erase(iterT);
			return true;
		}
	}
	return false;
}

//add/edit/delete account
bool Manager::addAccount(string newAcc, double newAccDep)
{
	list<string>::iterator iter;
	list<double>::iterator iterD = getAccDepIter("delete");
	//deal with "delete" acct special case at initialization
	if(newAcc == "delete")
	{
		*iterD = newAccDep;
		return true;
	}
	for(iter=_accAddr->begin(); iter!=_accAddr->end(); iter++)
	{
		//if acct name has existed
		if(*iter == newAcc)
			return false;
	}
	_accAddr->push_back(newAcc);
	_accDepAddr->push_back(newAccDep);
	return true;
}
bool Manager::editAccountName(string OLD, string NEW)
{
	if(OLD == NEW)
		return true;
	if(NEW == "" || OLD == "delete" || OLD == "Cash")
		return false;
	if(_accAddr->empty())
		return false;

	list<string>::iterator iter;
	for(iter=_accAddr->begin(); iter!=_accAddr->end(); iter++)
	{
		//if acct name has existed
		if(*iter == NEW)
			return false;
	}
	for(iter=_accAddr->begin(); iter!=_accAddr->end(); iter++)
	{
		if(*iter == OLD)
			*iter = NEW;
	}

	History FULL, ACC;
	if(!(_recordAddr->empty()))
	{
		FULL = AllTransaction();
		ACC = FULL.filterAccount(OLD);
	}
	//update transaction list
	if(!((ACC.getTransList())->empty()))
	{
		list<Transaction*>::iterator iterT;
		for(iterT=(ACC.getTransList())->begin(); iterT!=(ACC.getTransList())->end(); iterT++)
		{
			switch((*iterT)->getType())
			{
			case INCOME:
			case EXPENSE:
				(*iterT)->setAccount(NEW);
				break;
			case TRANSFER:
				if((*iterT)->getAccountFrom() == OLD)
					(*iterT)->setAccountFrom(NEW);
				else if((*iterT)->getAccountTo() == OLD)
					(*iterT)->setAccountTo(NEW);
				break;
			}
		}
	}
	return true;
}
bool Manager::delAccount(string OLD)
{
	if(OLD == "delete" || OLD == "Cash")
		return false;

	list<string>::iterator iterA;
	list<double>::iterator iterD,iterDELETE;
	if(!(_accAddr->empty()))
	{
		for(iterA=_accAddr->begin(), iterD=_accDepAddr->begin(); iterA!=_accAddr->end(); iterA++,iterD++)
		{
			if(*iterA == OLD)
			{
				//add OLD balance to "Cash"
				double balance = getAccBalance(OLD);
				Transfer* T = new Transfer(balance, *getSNAddr(),NOW(), OLD, "Cash");
				addTransaction(T);
				delete T;
		
				//add initial deposit to "delete"
				iterDELETE = getAccDepIter("delete");
				*iterDELETE += *iterD;

				//change all acct name to "delete"
				changeAccName(OLD,"delete");

				//delete acct name and deposit from the list
				_accAddr->erase(iterA);
				_accDepAddr->erase(iterD);
				return true;
			}
		}
		return false;
	}
}

//add/edit/delete category
bool Manager::addCategory(string newCat)
{
	list<string>::iterator iter;
	for(iter=_catAddr->begin(); iter!=_catAddr->end(); iter++)
	{
		//if category name has existed
		if(*iter == newCat)
			return false;
	}
	_catAddr->push_back(newCat);
	return true;
}
bool Manager::editCategory(string OLD, string NEW)
{
	if(OLD == NEW)
		return true;
	if(NEW == "" || OLD == "Income" || OLD == "Transfer" || OLD == "Others")
		return false;
	if(_catAddr->empty())
		return false;

	list<string>::iterator iter;
	for(iter=_catAddr->begin(); iter!=_catAddr->end(); iter++)
	{
		//if cat name has exised
		if(*iter == NEW)
			return false;
	}
	for(iter=_catAddr->begin(); iter!=_catAddr->end(); iter++)
	{
		if(*iter == OLD)
			*iter = NEW;
	}
	History FULL, CAT;
	if(!(_recordAddr->empty()))
	{
		FULL = AllTransaction();
		CAT = FULL.filterCategory(OLD);
	}
	if(!((CAT.getTransList())->empty()))
	{
		list<Transaction*>::iterator iterT;
		for(iterT=(CAT.getTransList())->begin(); iterT!=(CAT.getTransList())->end(); iterT++)
			(*iterT)->setCategory(NEW);
	}
	return true;
}
bool Manager::delCategory(string OLD)
{
	if(OLD == "Income" || OLD == "Others" || OLD == "Transfer")
		return false;
	list<string>::iterator iter;
	if(!(_catAddr->empty()))
	{
		for(iter=_catAddr->begin(); iter!=_catAddr->end(); iter++)
		{
			if(*iter == OLD)
			{
				//change all category name to "Others"
				changeCatName(OLD,"Others");
				//delete category from the list
				_catAddr->erase(iter);
				return true;
			}
		}
		return false;
	}
}
	//@author A0090982W
//consolidating functions
History Manager::AllTransaction()
{
	History AllTransaction;
	list<Transaction>::iterator iter;
	if(!_recordAddr->empty())
	{
		for(iter=_recordAddr->begin(); iter!=_recordAddr->end(); iter++)
			AllTransaction.add(&*iter);
	}
	return AllTransaction;
}
History Manager::currMonth(int year, int month)
{
	History currMonth;

	struct dateTime start, end;
	start.year = end.year = year;
	start.month = end.month = month;
	start.day = 1;
	start.hour = 0;
	start.minute = 0;

	end.day = 31;
	end.hour = 23;
	end.minute = 59;

	currMonth = (this->AllTransaction()).filterDate(start, end);
	return currMonth;
}
History Manager::today(int year, int month, int day)
{
	History today;

	struct dateTime start, end;
	start.year = end.year = year;
	start.month = end.month = month;
	start.day = end.day = day;

	start.hour = 0;
	start.minute = 0;

	end.hour = 23;
	end.minute = 59;

	today = this->AllTransaction();
	today = today.filterDate(start, end);
	return today;
}
double Manager::getAccBalance(string Acc)
{
	double Balance=0;
	History tempH = AllTransaction().filterAccount(Acc);
	Balance += *getAccDepIter(Acc);
	Balance += tempH.getTotalIncome();
	Balance -= tempH.getTotalExpense();

	tempH = tempH.filterType(TRANSFER);
	list<Transaction*>::iterator iterT;
	for(iterT=tempH.getTransList()->begin(); iterT!=tempH.getTransList()->end(); iterT++)
	{
		if((*iterT)->getAccountFrom()==Acc)
			Balance -= (*iterT)->getAmount();
		if((*iterT)->getAccountTo()==Acc)
			Balance += (*iterT)->getAmount();
	}
	return Balance;
}
double Manager::getTotInitialDep()
{
	double initialDeposit=0;
	list<double>::iterator iterD;
	for(iterD=getAccDep()->begin(); iterD!=getAccDep()->end(); iterD++)
	{
		initialDeposit += *iterD;
	}
	return initialDeposit;
}
double Manager::getTotBalance()
{
	double totBal;
	History temp=AllTransaction();
	totBal = temp.getTotalIncome()-temp.getTotalExpense();
	totBal += getTotInitialDep();
	totBal -=getAccBalance("delete");
	return totBal;
}
struct dateTime Manager::NOW()
{
	struct dateTime now;
	struct tm *sysTime;
	time_t *rawSysTime;
	rawSysTime = new time_t;
	time(rawSysTime);
	sysTime = localtime(rawSysTime);
	now.year = (sysTime->tm_year)+1900;
	now.month = (sysTime->tm_mon)+1;
	now.day = sysTime->tm_mday;
	now.hour = sysTime->tm_hour;
	now.minute = sysTime->tm_min;
	return now;
}
bool Manager::enoughBal(string acc, double amt)
{
	return (getAccBalance(acc)>=amt);
}

//@author A0091791X
void Manager::incrementSN()
{
	(*_SN)++;
}
void Manager::changeAccName(string OLD, string NEW)
{
	if(_accAddr->empty())
		return;

	History ACC;
	ACC=AllTransaction().filterAccount(OLD);
	if(!(ACC.getTransList()->empty()))
	{
		list<Transaction*>::iterator iterT;
		for(iterT=(ACC.getTransList())->begin(); iterT!=ACC.getTransList()->end(); iterT++)
		{
			switch((*iterT)->getType())
			{
			case INCOME:
			case EXPENSE:
				(*iterT)->setAccount(NEW);
			case TRANSFER:
				if((*iterT)->getAccountFrom()==OLD)
					(*iterT)->setAccountFrom(NEW);
				if((*iterT)->getAccountTo()==OLD)
					(*iterT)->setAccountTo(NEW);
				break;
			default:
				break;
			}
		}
	}

}
void Manager::changeCatName(string OLD, string NEW)
{
	if(_catAddr->empty())
		return;

	History CAT;
	CAT=AllTransaction().filterCategory(OLD);
	if(!(CAT.getTransList()->empty()))
	{
		list<Transaction*>::iterator iterT;
		for(iterT=(CAT.getTransList())->begin(); iterT!=CAT.getTransList()->end(); iterT++)
				(*iterT)->setCategory(NEW);
	}
}

bool Manager::checkDouble(string test)
{
	string num;
	int noOfDot=0;
	int noOfDig=0;

	//+/- at the beginning
	if(test[0]=='+'||test[0]=='-')
		num = test.substr(1);
	else
		num = test;

	//only "0"~"9", "."
	for(int i=0; i<num.length(); i++)
	{
		if(num[i]!='0' && num[i]!='1' && num[i]!='2' && num[i]!='3' && num[i]!='4'
			&& num[i]!='5'&& num[i]!='6' && num[i]!='7' && num[i]!='8' && num[i]!='9' && num[i]!='.')
			return false;
		if(num[i]=='.')
			noOfDot++;
		else
			noOfDig++;
	}


	//no of "." <=1, no of digit>0
	if(noOfDot>=2 || noOfDig<=0)
		return false;
	else
		return true;
}
bool Manager::checkDP(string num)
{
	int dp=0;
	int dot=0;
	if(num.find('.')==string::npos)
		return true;
	else
		if(num.length()-num.find('.')>3)
			return false;
	return true;
}

void Manager::pushUndo(int type, Transaction T)//deleted transaction, transaction before modification, transaction added 
{
	Action act(type,T);
	_action.push(act);
}
void Manager::performUndo()
{
	if(_action.empty()==true)
		return;

	Action tempA = _action.top();
	switch (tempA.getType())
	{
	case ADD:
		{
			//delete
			if(delTransaction(findTransaction(tempA.getTransaction().getSerialNo())))
			{
				//pop twice action list
				_action.pop();
				_action.pop();
			}
			break;
		}
	case EDIT:
		{
			//revert back
			if(editTransaction(tempA.getTransaction().getSerialNo(), tempA.getTransaction()))
			{
				//pop twice action list
				_action.pop();
				_action.pop();
			}
				break;
		}
	case DELETE:
		{
		//add
			if(addTransaction(&(tempA.getTransaction())))
			{
		//pop twice action list
				_action.pop();
				_action.pop();
			}
			break;
		}
	}
}
void Manager::clearActionStack()
{
	while(!_action.empty())
		_action.pop();
}